# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: leon

import collections
import logging

PerProductLimitPriceCheckerStatus = collections.namedtuple('PerProductLimitPriceCheckerStatus',
                                                           [
                                                               'product',
                                                               'Limit_price_valid',
                                                               'ticker_count',
                                                               'last_received_at',
                                                           ])


class PerProductLimitPriceChecker(object):
  def __init__(self, *, product, logger=None):
    self._product = product

    self._last_received_at = None
    self._last_bid0 = None
    self._last_ask0 = None
    self._last_limit_low = None
    self._last_limit_high = None
    self._count = 0

    # TODO(leon): Parameterized
    self._min_mid_limit_spread_ratio = 0.005
    self._min_limit_low_high_spread_ratio = 0.01

    self._logger = logger or logging.getLogger(__name__)

  @property
  def valid(self):
    return self._check_validity()

  @property
  def count(self):
    return self._count

  def _check_validity(self):
    if not all((self._last_bid0, self._last_ask0, self._last_limit_low, self._last_limit_high)):
      return True

    mid_price = (self._last_bid0 + self._last_ask0) / 2

    if (mid_price - self._last_limit_low) / mid_price < self._min_mid_limit_spread_ratio:
      self._logger.warning('%s: mid_price=%f, limit_low=%f, too close.',
                           self._product,
                           mid_price,
                           self._last_limit_low)
      return False
    if (self._last_limit_high - mid_price) / mid_price < self._min_mid_limit_spread_ratio:
      self._logger.warning('%s: mid_price=%f, limit_high=%f, too close.',
                           self._product,
                           mid_price,
                           self._last_limit_high)
      return False
    if (self._last_limit_high
        - self._last_limit_low) / mid_price < self._min_limit_low_high_spread_ratio:
      self._logger.warning('%s: limit_low=%f, limit_high=%f, too close.',
                           self._product,
                           self._last_limit_low,
                           self._last_limit_high)
      return False
    return True

  def on_book(self, *, book, timestamp):
    try:
      self._last_bid0 = book.bid0().price
      self._last_ask0 = book.ask0().price
    except (AttributeError, IndexError):
      self._last_bid0 = None
      self._last_ask0 = None

  def on_ticker(self, *, ticker_event, timestamp):
    self._last_received_at = timestamp
    self._last_limit_low = ticker_event.limit_low
    self._last_limit_high = ticker_event.limit_high
    self._count += 1

  def get_status(self, *, timestamp):
    return PerProductLimitPriceCheckerStatus(product=self._product,
                                             Limit_price_valid=self.valid,
                                             ticker_count=self.count,
                                             last_received_at=self._last_received_at)
